﻿using Microsoft.AspNetCore.Hosting.Server;
using Newtonsoft.Json;
using Polaris.Common.Extension;
using Polaris.Domain.Model.Entity.Base;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Polaris.Common.RedisHelper
{
    public class RedisHelper
    {
        private static readonly IDatabase _redisDb = RedisManager.Instance().GetDatabase();
        private static readonly StackExchange.Redis.IServer _server = RedisManager.Instance().GetServer(AppConfig.GetConfigInfo("ConnectionStrings:Redis"));

        #region String 可以设置过期时间

        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public static bool SetStringKey(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return _redisDb.StringSet(key, value, expiry);
        }

        /// <summary>
        /// 保存单个key value 异步
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public static async Task<bool> SetStringKeyAsync(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return await _redisDb.StringSetAsync(key, value, expiry);
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="arr">key</param>
        /// <returns></returns>
        public static bool SetStringKey(KeyValuePair<RedisKey, RedisValue>[] arr)
        {
            return _redisDb.StringSet(arr);
        }

        /// <summary>
        /// 保存多个key value 异步
        /// </summary>
        /// <param name="arr">key</param>
        /// <returns></returns>
        public static async Task<bool> SetStringKeyAsync(KeyValuePair<RedisKey, RedisValue>[] arr)
        {
            return await _redisDb.StringSetAsync(arr);
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool SetStringKey<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
            string json = JsonConvert.SerializeObject(obj);
            return _redisDb.StringSet(key, json, expiry);
        }

        /// <summary>
        /// 保存一个对象 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static async Task<bool> SetStringKeyAsync<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
            string json = JsonConvert.SerializeObject(obj);
            return await _redisDb.StringSetAsync(key, json, expiry);
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>

        public static RedisValue GetStringKey(string key)
        {
            return _redisDb.StringGet(key);
        }

        /// <summary>
        /// 获取单个key的值 异步
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>

        public static async Task<RedisValue> GetStringKeyAsync(string key)
        {
            return await _redisDb.StringGetAsync(key);
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public static RedisValue[] GetStringKey(List<RedisKey> listKey)
        {
            return _redisDb.StringGet(listKey.ToArray());
        }

        /// <summary>
        /// 获取多个Key 异步
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public static async Task<RedisValue[]> GetStringKeyAsync(List<RedisKey> listKey)
        {
            return await _redisDb.StringGetAsync(listKey.ToArray());
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetStringKey<T>(string key)
        {
            return JsonConvert.DeserializeObject<T>(_redisDb.StringGet(key));
        }

        /// <summary>
        /// 获取一个key的对象 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T> GetStringKeyAsync<T>(string key)
        {
            return JsonConvert.DeserializeObject<T>(await _redisDb.StringGetAsync(key));
        }

        /// <summary>
        /// 检查某个值是否存在于 Redis Set 集合中 异步
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static async Task<bool> SetContainsAsync(string key, string value)
        {
            return await _redisDb.SetContainsAsync(key, value);
        }

        /// <summary>
        /// 添加到 Redis Set 集合中 异步
        /// 多值集合 Set内元素唯一
        /// 注意：过期时间会重置 例如第一次会在30天过期，如果第二次传递了过期时间，会重置从现在到30天后过期
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static async Task<bool> SetAddAsync(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            if (expiry != null)
            {
                await _redisDb.KeyExpireAsync(key, expiry);
            }
            return await _redisDb.SetAddAsync(key, value);
        }

        #endregion

        #region Hash

        /// <summary>
        /// 保存一个集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="list">数据集合</param>
        /// <param name="getModelId"></param>
        public static void HashSet<T>(string key, List<T> list, Func<T, string> getModelId)
        {
            List<HashEntry> listHashEntry = new List<HashEntry>();
            foreach (var item in list)
            {
                string json = JsonConvert.SerializeObject(item);
                listHashEntry.Add(new HashEntry(getModelId(item), json));
            }
            _redisDb.HashSet(key, listHashEntry.ToArray());
        }

        /// <summary>
        /// 保存一个集合 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="list">数据集合</param>
        /// <param name="getModelId"></param>
        public static async Task HashSetAsync<T>(string key, List<T> list, Func<T, string> getModelId)
        {
            List<HashEntry> listHashEntry = new List<HashEntry>();
            foreach (var item in list)
            {
                string json = JsonConvert.SerializeObject(item);
                listHashEntry.Add(new HashEntry(getModelId(item), json));
            }
            await _redisDb.HashSetAsync(key, listHashEntry.ToArray());
        }

        /// <summary>
        /// 获取Hash中的单个key的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="hasFildValue">RedisValue</param>
        /// <returns></returns>
        public static T GetHashKey<T>(string key, string hasFildValue)
        {
            if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(hasFildValue))
            {
                RedisValue value = _redisDb.HashGet(key, hasFildValue);
                if (!value.IsNullOrEmpty)
                {
                    return JsonConvert.DeserializeObject<T>(value);
                }
            }
            return default(T);
        }

        /// <summary>
        /// 获取Hash中的单个key的值 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="hasFildValue">RedisValue</param>
        /// <returns></returns>
        public static async Task<T> GetHashKeyAsync<T>(string key, string hasFildValue)
        {
            if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(hasFildValue))
            {
                RedisValue value = await _redisDb.HashGetAsync(key, hasFildValue);
                if (!value.IsNullOrEmpty)
                {
                    return JsonConvert.DeserializeObject<T>(value);
                }
            }
            return default(T);
        }

        /// <summary>
        /// 获取hash中的多个key的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="listhashFields">RedisValue value</param>
        /// <returns></returns>
        public static List<T> GetHashKey<T>(string key, List<RedisValue> listhashFields)
        {
            List<T> result = new List<T>();
            if (!string.IsNullOrWhiteSpace(key) && listhashFields.Count > 0)
            {
                RedisValue[] value = _redisDb.HashGet(key, listhashFields.ToArray());
                foreach (var item in value)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        result.Add(JsonConvert.DeserializeObject<T>(item));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hash中的多个key的值 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="listhashFields">RedisValue value</param>
        /// <returns></returns>
        public static async Task<List<T>> GetHashKeyAsync<T>(string key, List<RedisValue> listhashFields)
        {
            List<T> result = new List<T>();
            if (!string.IsNullOrWhiteSpace(key) && listhashFields.Count > 0)
            {
                RedisValue[] value = await _redisDb.HashGetAsync(key, listhashFields.ToArray());
                foreach (var item in value)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        result.Add(JsonConvert.DeserializeObject<T>(item));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有Redis key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> GetHashAll<T>(string key)
        {
            List<T> result = new List<T>();
            RedisValue[] arr = _redisDb.HashKeys(key);
            foreach (var item in arr)
            {
                if (!item.IsNullOrEmpty)
                {
                    result.Add(JsonConvert.DeserializeObject<T>(item));
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有Redis key 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<List<T>> GetHashAllAsync<T>(string key)
        {
            List<T> result = new List<T>();
            RedisValue[] arr = await _redisDb.HashKeysAsync(key);
            foreach (var item in arr)
            {
                if (!item.IsNullOrEmpty)
                {
                    result.Add(JsonConvert.DeserializeObject<T>(item));
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> HashGetAll<T>(string key)
        {
            List<T> result = new List<T>();
            HashEntry[] arr = _redisDb.HashGetAll(key);
            foreach (var item in arr)
            {
                if (!item.Value.IsNullOrEmpty)
                {
                    result.Add(JsonConvert.DeserializeObject<T>(item.Value));
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有的值 异步
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<List<T>> HashGetAllAsync<T>(string key)
        {
            List<T> result = new List<T>();
            HashEntry[] arr = await _redisDb.HashGetAllAsync(key);
            foreach (var item in arr)
            {
                if (!item.Value.IsNullOrEmpty)
                {
                    result.Add(JsonConvert.DeserializeObject<T>(item.Value));
                }
            }
            return result;
        }

        /// <summary>
        /// 存储分页数据到 Redis Hash
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="pagination"></param>
        /// <returns></returns>
        public static async Task CachePaginationAsync<T>(string key, PaginationInfo<T> pagination)
        {
            var hashEntries = new[]
            {
                new HashEntry("TotalNum", pagination.TotalNum),
                new HashEntry("PageIndex", pagination.PageIndex),
                new HashEntry("PageSize", pagination.PageSize),
                new HashEntry("Result", JsonConvert.SerializeObject(pagination.Result))
            };

            await _redisDb.HashSetAsync(key, hashEntries);
            var baseExpiry = TimeSpan.FromMinutes(30);
            var randomOffset = TimeSpan.FromMinutes(new Random().Next(-5, 5));
            await _redisDb.KeyExpireAsync(key, baseExpiry + randomOffset);
        }

        /// <summary>
        /// 从 Redis Hash 获取分页数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<PaginationInfo<T>> GetPaginationAsync<T>(string key)
        {
            var hashFields = await _redisDb.HashGetAllAsync(key);

            if (hashFields.Length == 0) return null;

            var pagination = new PaginationInfo<T>
            {
                TotalNum = (int)hashFields.FirstOrDefault(x => x.Name == "TotalNum").Value,
                PageIndex = (int)hashFields.FirstOrDefault(x => x.Name == "PageIndex").Value,
                PageSize = (int)hashFields.FirstOrDefault(x => x.Name == "PageSize").Value,
                Result = JsonConvert.DeserializeObject<List<T>>(
                    hashFields.FirstOrDefault(x => x.Name == "Result").Value)
            };

            return pagination;
        }

        /// <summary>
        /// 删除hasekey
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashField"></param>
        /// <returns></returns>
        public static bool DeleteHase(RedisKey key, RedisValue hashField)
        {
            return _redisDb.HashDelete(key, hashField);
        }

        /// <summary>
        /// 删除hasekey 异步
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashField"></param>
        /// <returns></returns>
        public static async Task<bool> DeleteHaseAsync(RedisKey key, RedisValue hashField)
        {
            return await _redisDb.HashDeleteAsync(key, hashField);
        }

        /// <summary>
        /// 删除匹配通配符的键 例如：user:* 就是删除所以已"user:"开头的键
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static async Task DeleteKeysByPatternAsync(string pattern)
        {
            try
            {
                var keysToDelete = new List<RedisKey>();
                // 使用 SCAN 命令获取匹配的键
                //var cursor = _server.Keys(pattern: pattern, pageSize: 1000);

                long cursor = 0;
                const int pageSize = 1000; // 每批次扫描数量
                do
                {
                    // 1. 执行 SCAN 命令（正确参数顺序）
                    var result = await _redisDb.ExecuteAsync(
                        "SCAN",
                        cursor.ToString(),
                        "MATCH", pattern,
                        "COUNT", pageSize.ToString()
                    );

                    // 2. 解析返回值
                    var response = (RedisResult[])result;
                    cursor = long.Parse((string)response[0]); // 游标转为 long
                    var keys = (RedisKey[])response[1];       // 提取键数组

                    if (keys.Length == 0) continue;

                    // 3. 批量删除（使用 UNLINK 替代 DEL 提升性能）
                    await _redisDb.KeyDeleteAsync(keys); // 直接传递键数组

                    Console.WriteLine($"Deleted {keys.Length} keys: {string.Join(", ", keys)}");
                }
                while (cursor != 0); // 游标为 0 时终止
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        // 异步方法：将对象存储为Redis哈希结构，可选设置过期时间
        public static async Task<bool> SetHashAsync<T>(string key, T value, TimeSpan? expiry)
        {
            // 使用反射获取对象的所有属性，并转换为HashEntry数组
            // 注意：Redis 的哈希字段值不允许为null
            var entries = typeof(T).GetProperties()
                .Select(p => new HashEntry(p.Name, p.GetValue(value)?.ToString() ?? string.Empty))
                .ToArray();

            // 将哈希条目写入Redis
            await _redisDb.HashSetAsync(key, entries);

            // 问题3：设置过期时间非原子操作，若此处失败会导致无过期时间但返回成功
            if (expiry.HasValue) await _redisDb.KeyExpireAsync(key, expiry);

            // 始终返回true，无法感知操作是否真正成功
            return true;
        }

        // 异步方法：从Redis哈希结构中读取数据并转换为对象
        public static async Task<T> GetHashAsync<T>(string key) where T : class, new()
        {
            // 获取所有哈希字段
            var entries = await _redisDb.HashGetAllAsync(key);
            if (!entries.Any()) return null;

            // 创建目标对象实例
            var obj = new T();
            // 将属性转为字典便于查找
            var properties = typeof(T).GetProperties().ToDictionary(p => p.Name, p => p);

            foreach (var entry in entries)
            {
                if (properties.TryGetValue(entry.Name, out var prop))
                {
                    var value = Convert.ChangeType(entry.Value, prop.PropertyType);
                    prop.SetValue(obj, value);
                }
            }
            return obj;
        }

        #endregion

        #region key

        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public static bool KeyDelete(string key)
        {
            return _redisDb.KeyDelete(key);
        }

        /// <summary>
        /// 删除单个key 异步
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public static async Task<bool> KeyDeleteAsync(string key)
        {
            return await _redisDb.KeyDeleteAsync(key);
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public static long KeyDelete(RedisKey[] keys)
        {
            return _redisDb.KeyDelete(keys);
        }

        /// <summary>
        /// 删除多个key 异步
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public static async Task<long> KeyDeleteAsync(RedisKey[] keys)
        {
            return await _redisDb.KeyDeleteAsync(keys);
        }

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public static bool KeyExists(string key)
        {
            return _redisDb.KeyExists(key);
        }

        /// <summary>
        /// 判断key是否存储 异步
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public static async Task<bool> KeyExistsAsync(string key)
        {
            return await _redisDb.KeyExistsAsync(key);
        }

        /// <summary>
        /// 重新命名key
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public static bool KeyRename(string key, string newKey)
        {
            return _redisDb.KeyRename(key, newKey);
        }

        /// <summary>
        /// 重新命名key 异步
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public static async Task<bool> KeyRenameAsync(string key, string newKey)
        {
            return await _redisDb.KeyRenameAsync(key, newKey);
        }

        #endregion


        /// <summary>
        /// 追加值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void StringAppend(string key, string value)
        {
            ////追加值，返回追加后长度
            long appendlong = _redisDb.StringAppend(key, value);
        }

        /// <summary>
        /// 追加值 异步
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static async Task StringAppendAsync(string key, string value)
        {
            ////追加值，返回追加后长度
            long appendlong = await _redisDb.StringAppendAsync(key, value);
        }

    }
}
